﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net.Sockets;
using Communication;
using Communication.Packets;
using Communication.Protocol.General;
using Communication.Tcp;
using ConnectServer.Protocol;
using Utilities;

namespace ConnectServer.Management
{
    /// <summary>
    /// ConnectServer's manager
    /// </summary>
    class Manager
    {
        #region Ctor
        public Manager()
        {
            this.m_logger = Logger.Instance;
            this.m_logger.Log("Initializing...");
            this.m_packetFinder = PacketFinder.Instance;
            this.m_config = Config.Instance;
			this.m_users = new List<User>();
			
            this.m_server = new Server(this.m_config.Port, this.m_config.MaxConnections, this.m_config.BindIP, 1, this.m_config.Verbose);
            this.m_server.OnConnect += new Server.ServerEventHandler<EventArgs>(OnConnect);
            this.m_server.OnDisconnect += new Server.ServerEventHandler<EventArgs>(OnDisconnect);
            this.m_server.OnError += new Server.ServerEventHandler<ErrorEventArgs>(OnError);
            this.m_server.OnReceive += new Server.ServerEventHandler<RecvEventArgs>(OnReceive);

            this.m_server.Start();

            this.m_logger.Log("Initialized.");
        }
        #endregion

        #region Privates
        /// <summary>
        /// Gets user instance with the specified client
        /// </summary>
        /// <param name="client">The client to search for</param>
        /// <returns>User's instance</returns>
        private User getUserBySocket(TcpClient client) 
		{
			foreach (User user in this.m_users) 
			{
				if (user.Client == client) 
				{
					return user;
				}
			}
			return null;
		}

        /// <summary>
        /// Occurs on receive of a packet. 
        /// </summary>
        /// <param name="client"></param>
        /// <param name="e"></param>
        private void OnReceive(TcpClient client, RecvEventArgs e)
        {
            User user = getUserBySocket(client);
            if (user == null)
            {
                // client disconnected?
            }
            try
            {
                Type receivedPacketType = this.m_packetFinder.GetPacketType(e.Buffer);
                user.HandlePacket(receivedPacketType, e.Buffer);
            }
            catch (UnknownPacketTypeError)
            {
                user.LogError("Unknown packet received.");
            }
        }

        /// <summary>
        /// Occurs when error issued.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="e"></param>
        private void OnError(TcpClient client, ErrorEventArgs e)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Occurs when client disconnectes.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="e"></param>
        private void OnDisconnect(TcpClient client, EventArgs e)
        {
            User user = getUserBySocket(client);
			if (user == null) 
			{
				// Invalid user disconnected? double disconnection?
			}
			this.m_users.Remove(user);
        }

        /// <summary>
        /// Occurs when client connects.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="e"></param>
        private void OnConnect(TcpClient client, EventArgs e)
        {
            // Register new client
			User user = new User(client, this.m_server);
			this.m_users.Add(user);

            m_logger.Log("Established connection with {0}", client.Client.RemoteEndPoint);

			user.Send(new AckPacket());
        }
        #endregion

        #region Fields
        private List<User> m_users;
        private Config m_config;
        private Logger m_logger;
        private Server m_server;
        private PacketFinder m_packetFinder;
        #endregion
    }
}
